/*-----------------------------------------
// Declaration of the Header files required.
------------------------------------------*/



#include "syscall.h"


/*----------------------------------------------------------
// Declare global variables and Data Structures Below
----------------------------------------------------------*/

#define MAX_NO_OF_CLERKS 5
#define MAX_NO_OF_CASHREGISTERS 5
#define MAX_NO_OF_CUSTOMERS 20
#define MAX_NO_OF_AISLE 10
#define MAX_NO_SB 10
#define FALSE 0
#define TRUE 1


/* status values for cRSt
 * enum cRStEnum{NOT_SET, CLERK_WAITING, CASHREG_WAITING }; */
#define	NOT_SET 			0
#define CLERK_WAITING		1
#define CASHREG_WAITING		2

/*status values for clerstatus
 * enum ckStEnum{CLERK_FREE, CLERK_BUSY, CLERK_ONBREAK, CLERK_COMING_FROM_BREAK}; */
#define CLERK_FREE					0
#define CLERK_BUSY					1
#define CLERK_ONBREAK				2
#define CLERK_COMING_FROM_BREAK		3


#define MAX_NAME_SIZE   	30
/*buffer to hold the names of lock and CV's and MV's*/
char name[MAX_NAME_SIZE];

/*Customer data sturcture*/
typedef struct customer
{
	int noOfItemsToBePurchased;
	int itemsToBePurchased[10];
	int customerReturning;
	/*customer()
	{
		noOfItemsToBePurchased=(int)((GetRand()%10)+1);
		for(int i=0;i<noOfItemsToBePurchased;i++)
		{
			itemsToBePurchased[i]=(int)(GetRand()%10);
		}
		if(GetRand()%5==4){
			customerReturning=true;
		}
		else{
			customerReturning=false;
		}
	}*/

}customerDataTemplate;




/* cashregister data-structure*/

/*typedef struct cashregister
{

	/* only clrkLock[] should access this data member
	int customerAmount;
	/* only mCshRgLk should access this data member
	int totalAmount;
	/*totalsales at this cash counter
	int totalSalesAtCashRegister;

	cashregister(){

		customerAmount = 0;
		totalAmount = 0;
		totalSalesAtCashRegister = 0;
	}
}cashRegisterTemplate;*/

int crCuA[MAX_NO_OF_CASHREGISTERS];
int crTtl[MAX_NO_OF_CASHREGISTERS];
int crTSl[MAX_NO_OF_CASHREGISTERS];


int cuTCtr;
int cRTCtr;
int ckTCtr;
int sbTCtr;

int cusTCtrLk;
int cRgTCtrLk;
int clkTCtrLk;
int sbTCtrLk;

int testcase;
int noOfClerks;		/* values will change dynamically*/
int noOfCashReg;
int noOfCustomers;
int noOfStockBoys;
int noOfAisle;
/*cashRegisterTemplate cashRegister[MAX_NO_OF_CASHREGISTERS];*/
customerDataTemplate customerData[MAX_NO_OF_CUSTOMERS];



int itemsInStoreInfo[MAX_NO_OF_AISLE]={1,2,3,4,5,6,7,8,9,10} ;

/*------------------------------------------------------------------------
 Declare Locks, CV and Monitor Variables for Customer/Clerk Interaction
------------------------------------------------------------------------*/


/*Lock to  print multiple PRINT Syscalls*/
int printLock;

/*Lock for mutual exclusion on entering line.*/
int clrkLnLk;
/*Locks for each clerk interaction.*/
int clrkLock[MAX_NO_OF_CLERKS];
/*condition to wait on a line for checkout*/
int clrkLnCV[MAX_NO_OF_CLERKS];
/*condition to interaction btw one customer and clerk at a time.*/
int clrkTkCV[MAX_NO_OF_CLERKS];
/*Condition Variable for Clerks waiting on a break*/
int clOBrkCV;
/* maintains the length of each line.*/
int ckLL[MAX_NO_OF_CASHREGISTERS];
/*maintains the status of each clerk.*/
int ckSt[MAX_NO_OF_CLERKS];


/*all to be used for checkout process. To be used under clrkLock[i].*/
int itemId[MAX_NO_OF_CLERKS];
/*to notify that the last item will be processed for each customer/clerk/cash register transaction.*/
int lstIt[MAX_NO_OF_CLERKS];
/*to store the value of customer id so that clerk and cash register know who they are serving.*/
int cuId[MAX_NO_OF_CLERKS];
/*to store the total bill amount for each customer/clerk/cash register transaction.*/
int rcBA[MAX_NO_OF_CLERKS];
/* will help each clerk and cash register know if the customer wants to buy or return items.*/
int isCuR[MAX_NO_OF_CLERKS];/*to check if it is buying or returning customer*/
/*to be used with clrkLnLk*/
int sgCk; /* To store Id of clerk who call manager when line is above 4*/




/*------------------------------------------------------------------------
 Declare Locks, CV and Monitor Variables for Clerk/CashRegister Interaction
------------------------------------------------------------------------*/

/*All these variables would be modified with the clrkLock[i]
differnet states a cash register can be in.*/

/*for clerk and cash reg interaction.*/
int cshRgsCV[MAX_NO_OF_CASHREGISTERS];
/*status array for all cash registers.*/
int cRSt[MAX_NO_OF_CASHREGISTERS];


/*------------------------------------------------------------------------------
// Declare Locks, CV and Monitor Variables for Manager/Cash Register Interaction
//------------------------------------------------------------------------------*/

/*Lock for Mutual Exclusion between Manager and Cash Register*/
/*No CV's or Monitor variables between them*/
int mCshRgLk[MAX_NO_OF_CASHREGISTERS];
int amNd[MAX_NO_OF_CASHREGISTERS];/*amount needed by cash register to give to customer returning items*/
int cshRgWCV[MAX_NO_OF_CASHREGISTERS];

/*------------------------------------------------------------------------------
// Declare Locks, CV and Monitor Variables for Customer/StockBoy/Aisle Interaction
//------------------------------------------------------------------------------*/


/* lock to check the status of stock boy maintianing the mutual exclusion.*/
int sbStatLk;
/* sbSt for maintianing the status of each stockboy	-1=free		-2=on break	>=0 In store doing stuff*/
int sbSt[MAX_NO_SB];
/*maintain the length of the customer needing help in each aisle.*/
int aHlpLL[MAX_NO_OF_AISLE];
/* to wait on the aisle for help.*/
int aslHlpCV[MAX_NO_OF_AISLE];

/*access stock room one at time.*/
int stckRmLk;
int stRS;/*to be used with stckRmLk*/
int stckRmCV;

/*access the restock list maintianing the data consistency.*/
int reStckLk;

/*lock for updating the Qty on each aisle.*/
int aslQtyLk[MAX_NO_OF_AISLE];
/*maintain the Qty on each aisle*/
int aslQty[MAX_NO_OF_AISLE];
/*if the Qty on aisle is empty, wait till restocked.*/
int aslEmtCV[MAX_NO_OF_AISLE];


/* Lock for the button when no SB available.*/
int noSBBtLk;
/* customers wait till SB is woken by manager.*/
int noSBBtCV;
/*stock boys wait till SB is woken by manager.*/
int sbOBrkCV;
/* maintains the length of the line waiting on the pushbutton.*/
int noSBLL;

/* lock on each SB.*/
int sbLock[MAX_NO_SB];
/* wait on SB.*/
int sbCV[MAX_NO_SB];
/*For the Customer and SB ping pong.*/
int sbCustQA[MAX_NO_SB][2];


/*------------------------------------------------------------------------------
// Declare Locks, CV and Monitor Variables for Manager Interaction
//------------------------------------------------------------------------------*/

/*Customer waiting when all clerks are on break*/
int custWtCV;/* This CV should be used with clrkLnLk*/
int alCkOB;
/*Clerks signaling manager to wakeup another clerk if their linelength>4*/
int wkUpCk;/* This CV should be used with clrkLnLk*/
/*variable modified by customers and read by manager to make note of end of simulation*/
int tlCuEx;







/*//=========StockBoy thread will begin here===========//*/
void StockBoy()
{
	int threadId;
	int myId;
	int i;

	Acquire(sbTCtrLk);

	threadId=GetMV(sbTCtr);
	Print2("SB counter is %d: so thread id is %d.\n",GetMV(sbTCtr),threadId);
	SetMV(sbTCtr, (GetMV(sbTCtr)+1));
	Release(sbTCtrLk);
	 myId = threadId;

	/*Stockboy thread buying some time for customers to buy items
	for(i=0; i<25; i++)
	{
		Yield();
	}
	*/

	while(1)
	{
		int myaisle=-1;	/* index to know the aisle that i need to restock*/
		Acquire(reStckLk);
		Acquire(printLock);
		Print1("StockBoy [%d] is checking if more items are to be re-stocked.\n",myId);
		Release(printLock);
		if(!IsListEmpty())/*if restock list is not empty then go to the stock room to get the item*/
		{
			myaisle = ListRemove();/*Remove the item to be restocked from the list*/
			Release(reStckLk);
			Acquire(printLock);
			Print2("StockBoy [%d] goes back to stock room for item [%d].\n",myId,myaisle);
			Release(printLock);
			/*Get into the stock room to obtain the item*/
			Acquire(stckRmLk);
			if(GetMV(stRS)==-1){
				SetMV(stRS, myId);
			}
			else{
				Acquire(printLock);
				Print2("StockBoy [%d] is waiting for StockBoy [%d] to leave the stock room.\n",myId, GetMV(stRS));
				Release(printLock);
				Wait(stckRmCV,stckRmLk);
			}
			Release(stckRmLk);
			/*Spend some time in the stock room*/
			Acquire(printLock);
			Print1("StockBoy [%d] is in stock room.\n",threadId);
			Release(printLock);
			for(i=0; i<1; i++)
			{
					Yield();
			}
			Acquire(printLock);
			Print2("StockBoy [%d] retrieve one new item [%d] from stock room.\n",threadId,myaisle);
			Release(printLock);
			Acquire(printLock);
			Print1("StockBoy [%d] leaves stock room.\n",threadId);
			Release(printLock);
			Acquire(stckRmLk);
			Signal(stckRmCV,stckRmLk);
			SetMV(stRS, -1);
			Release(stckRmLk);
			/*Out of the stock room*/
		}


		if(myaisle==-1)/*There is nothing to restock*/
		{
			Release(reStckLk);/*if no item to restock, release restock list*/
			Acquire(sbStatLk);
			Acquire(printLock);
			Print1("StockBoy [%d] is going on break.\n",threadId);
			Release(printLock);
			SetMV(sbSt[myId], -2);/*off to break*/

			Release(sbStatLk);

			/*on break*/
			Acquire(noSBBtLk);
			Wait(sbOBrkCV,noSBBtLk);
			Acquire(printLock);
			Print1("StockBoy [%d] got call from Manager.\n",threadId);
			Release(printLock);
			Release(noSBBtLk);

			/*Back to work*/
			Acquire(sbStatLk);
			SetMV(sbSt[myId], -1);/*default state*/
			Release(sbStatLk);

			/* coming back from break*/
			Acquire(noSBBtLk);
			while(GetMV(noSBLL)>0){
				SetMV(noSBLL, GetMV(noSBLL)-1);
				Signal(noSBBtCV,noSBBtLk);
				Acquire(sbStatLk);
				SetMV(sbSt[myId], -3);
				Acquire(sbLock[myId]);
				Release(sbStatLk);
				Release(noSBBtLk);
				Wait(sbCV[myId],sbLock[myId]);
				/*Ping-Pong*/

				Signal(sbCV[myId],sbLock[myId]);
				Release(sbLock[myId]);

				Acquire(noSBBtLk);/*Acquire this lock for the while loop*/
			}
			Release(noSBBtLk);
			continue;/*start from the infinite while loop*/
		}/*end of (if(myaisle==-1))*/

		else/*I have to restock an item*/
		{
			/*change my status to the aisle i am in*/
			Acquire(sbStatLk);
			SetMV(sbSt[myId], myaisle);
			Release(sbStatLk);

			/*Spending some time in the aisle		*/
			for(i=0; i<25; i++)
			{
				Yield();
			}
			/*Restock the item;*/
			Acquire(aslQtyLk[myaisle]);

				Acquire(printLock);
				Print3("Stockboy [%d] is restocking item [%d] on Aisle [%d].\n",threadId,myaisle,myaisle);
				Release(printLock);

			SetMV(aslQty[myaisle], GetMV(aslQty[myaisle])+1);
			Signal(aslEmtCV[myaisle],aslQtyLk[myaisle]);/*If any customer is waiting for an item, then signal him*/
			Release(aslQtyLk[myaisle]);

			/*See if any customer is waiting for my service*/
			Acquire(sbStatLk);
			while(GetMV(aHlpLL[myaisle])>0)
			{
				Signal(aslHlpCV[myaisle],sbStatLk);
				SetMV(aHlpLL[myaisle], GetMV(aHlpLL[myaisle])-1);	/*Decrement the aisle length*/
				SetMV(sbSt[myId], GetMV(sbSt[myId]) + MAX_NO_OF_AISLE);/*increment to signal customer that i am free*/
				Acquire(sbLock[myId]);
				Release(sbStatLk);
				Wait(sbCV[myId],sbLock[myId]);
				Acquire(printLock);
				Print2("StockBoy [%d] is approached by Customer [%d] for assistance.\n",threadId, GetMV(sbCustQA[myId][0]));
				Release(printLock);
				/*Time to play ping pong*/
				Acquire(printLock);
				Print3("StockBoy [%d] gave aisle [%d] to Customer [%d]",threadId, GetMV(sbCustQA[myId][1]), GetMV(sbCustQA[myId][0]));
				Print1("for item [%d].\n", GetMV(sbCustQA[myId][1]));
				Release(printLock);
				Signal(sbCV[myId],sbLock[myId]);
				Release(sbLock[myId]);

				/*Acquire sbStatLk again for the while*/
				Acquire(sbStatLk);
			}
			/*end of customer service*/
			SetMV(sbSt[myId], -1);
			Release(sbStatLk);
		}/*end of else block of restocking and customer service*/
	}/*end of infinite while loop*/
}/*end of function*/

/*===Stock Boy thread ends here================//




/*switch case for Sprintf */
void SwitchSprintf(int id, int index)
{

	switch(id){
		case 0:
		{
			name[index] = '0';
			break;
		}
		case 1:
		{
			name[index] = '1';
			break;
		}
		case 2:
		{
			name[index] = '2';
			break;
		}
		case 3:
		{
			name[index] = '3';
			break;
		}
		case 4:
		{
			name[index] = '4';
			break;
		}
		case 5:
		{
			name[index] = '5';
			break;
		}
		case 6:
		{
			name[index] = '6';
			break;
		}
		case 7:
		{
			name[index] = '7';
			break;
		}
		case 8:
		{
			name[index] = '8';
			break;
		}
		case 9:
		{
			name[index] = '9';
			break;
		}
		default:
			Print("Error: Sprintf incorrect id\n");
	}/*end of switch case*/

}

/* Sprintf code */
int Sprintf(char *idName, int id, int len)
{
	int i;
	int id1, id2;
	for(i=0; i<len; i++)
	{
		name[i] = idName[i];
	}
	/*Warning: maximum number for id is 99 */
	if(id<=9){
		SwitchSprintf(id, i);
		i++;
		name[i]='\0';
	}
	else if((id>=10) && (id<=99)){
		id1 = id/10;
		SwitchSprintf(id1, i);
		i++;
		id2 = id%10;
		SwitchSprintf(id2, i);
		i++;
		name[i]='\0';
	}
	else{
		Print("Error: Sprintf incorrect id. greater than 99");
		return -1;
	}
	return i;
}
/*
//============================================
//  Functions for the repeatable test cases
//============================================


//This part of the code generates the common test parameters needed for the simulation to run
// i.e. Condition Variables , Monitors & Locks*/
void generateCmnTestParams()
{
	int i=0,j=0;
	int len;



	Acquire(printLock);
	Print1("\n\nNumber of Clerks = [%d]\n",noOfClerks);
	Print1("Number of StockBoys = [%d]\n",noOfStockBoys);
	Print1("Number of CashRegisters = [%d]\n",noOfCashReg);
	Print1("Number of Customers = [%d]\n",noOfCustomers);
	Print("Number of Managers = [1]\n");
	Print("Number of Aisle = [10]\n");
    Print("Items:\n");
    Print("\tNumber - Price\n");

	for(j=0;j<MAX_NO_OF_AISLE;j++)
	{
		Print2("\t%d  -  %d  \n",j,itemsInStoreInfo[j]);
	}
    Print("\n\n");
	Release(printLock);


	clrkLnLk= CreateLock("clrkLnLk",8);
	sbStatLk = CreateLock("sbStatLk",8);
	stckRmLk = CreateLock("stckRmLk",8);
	stckRmCV = CreateCondition("stckRmCV",8);
	reStckLk = CreateLock("reStckLk",8);
	custWtCV = CreateCondition("custWtCV",8);
	for(i=0;i<MAX_NO_OF_AISLE;i++)
	{
		len = Sprintf("aslQtyLk",i,8);
		aslQtyLk[i]=CreateLock(name , len);

		len = Sprintf("aslEmtCV",i,8);
		aslEmtCV[i]=CreateCondition(name, len);

		len = Sprintf("aslHlpCV",i,8);
		aslHlpCV[i]=CreateCondition(name, len);
	}

	noSBBtLk =CreateLock("noSBBtLk",8);
	noSBBtCV = CreateCondition("noSBBtCV",8);

	sbOBrkCV = CreateCondition("sbOBrkCV",8);
	for(i=0;i<noOfStockBoys;i++)
	{
		len = Sprintf("sbLock",i,6);
		sbLock[i]=CreateLock(name, len);

		len = Sprintf("sbCV",i,4);
		sbCV[i]=CreateCondition(name, len);

	}
	for(i=0;i<noOfClerks;i++)
	{
		len = Sprintf("clrkLock",i,8);
		clrkLock[i]=CreateLock(name, len);

		len = Sprintf("clrkLnCV",i,8);
		clrkLnCV[i]=CreateCondition(name, len);

		len = Sprintf("clrkTkCV",i,8);
		clrkTkCV[i]=CreateCondition(name, len);
	}

	for(i=0;i<noOfCashReg;i++)
	{
		len = Sprintf("cshRgsCV",i,8);
		cshRgsCV[i]=CreateCondition(name, len);

		len = Sprintf("mCshRgLk",i,8);
		mCshRgLk[i]=CreateLock(name, len);

		len = Sprintf("cshRgWCV",i,8);
		cshRgWCV[i]=CreateCondition(name, len);
	}



	clOBrkCV=CreateCondition("clOBrkCV",8);

	for(i=0;i<noOfCustomers;i++)
	{
		customerData[i].noOfItemsToBePurchased=(int)((GetRand()%10)+1);
		for(j=0;j<customerData[i].noOfItemsToBePurchased;j++)
		{
			customerData[i].itemsToBePurchased[j]=(int)(GetRand()%10);
		}
		if(GetRand()%5==4){
			customerData[i].customerReturning=TRUE;
		}
		else{
			customerData[i].customerReturning=FALSE;
		}
	}



	/* Create Monitor variables*/
	cuTCtr = CreateMV("cuTCtr",6,0);
	cRTCtr = CreateMV("cRTCtr",6,0);
	ckTCtr = CreateMV("ckTCtr",6,0);
	sbTCtr = CreateMV("sbTCtr",6,0);

	for(i=0; i<noOfCashReg; i++){
		len = Sprintf("ckLL",i,4);
		ckLL[i] = CreateMV(name, len, 0);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("ckSt",i,4);
		ckSt[i] = CreateMV(name, len, CLERK_FREE);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("itId",i,4);
		itemId[i] = CreateMV(name, len, -1);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("lstIt",i,5);
		lstIt[i] = CreateMV(name, len, FALSE);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("cuId",i,4);
		cuId[i] = CreateMV(name, len, -1);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("rcBA",i,4);
		rcBA[i] = CreateMV(name, len, -1);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("isCuR",i,5);
		isCuR[i] = CreateMV(name, len, FALSE);
	}

	sgCk = CreateMV("sgCk", 4, -1);


	for(i=0; i<noOfCashReg; i++){
		len = Sprintf("crCuA",i,5);
		crCuA[i] = CreateMV(name, len, 0);

		len = Sprintf("crTtl",i,5);
		crTtl[i] = CreateMV(name, len, 0);

		len = Sprintf("crTSl",i,5);
		crTSl[i] = CreateMV(name, len, 0);
	}

	for(i=0; i<noOfCashReg; i++){
		len = Sprintf("cRSt",i,4);
		cRSt[i] = CreateMV(name, len, NOT_SET);

		len = Sprintf("amNd",i,4);
		amNd[i] = CreateMV(name, len, 0);
	}


	noSBLL = CreateMV("noSBLL",6,0);

	tlCuEx = CreateMV("tlCuEx",6,0);

	wkUpCk = CreateMV("wkUpCk",6,FALSE);

	alCkOB = CreateMV("alCkOB",6,FALSE);

	stRS = CreateMV("stRS",4,-1);


	for(i=0; i<MAX_NO_OF_AISLE; i++){
		len = Sprintf("aslQy",i,5);
		aslQty[i] = CreateMV(name, len, 3);

		len = Sprintf("aHlpL",i,5);
		aHlpLL[i] = CreateMV(name, len, 0);
	}

	for(i=0; i<noOfStockBoys; i++){
		len = Sprintf("sbSt",i,4);
		sbSt[i] = CreateMV(name, len, -1);
	}


	for(i=0; i<noOfStockBoys; i++){
		len = Sprintf("sbC0Q",i,5);
		sbCustQA[i][0] = CreateMV(name, len, 0);

		len = Sprintf("sbC1Q",i,5);
		sbCustQA[i][1] = CreateMV(name, len, 0);
	}


}

/*This test case runs the entire simulation with minimum requirements*/
void testcase12()
{


	noOfClerks=3;
	noOfCashReg=3;
	noOfCustomers=20;
	noOfStockBoys=3;

	generateCmnTestParams();
}



/*====================================================================================
// Function Problem2() which is called for simulation of the Part 2 of Assignment 1
// Contains the Test Case Selection Menu in it.
//====================================================================================*/


void main()
{


	printLock = CreateLock("printLock",9);
	cusTCtrLk=CreateLock("cusTCtrLk",9);
	cRgTCtrLk=CreateLock("cRgTCtrLk",9);
	clkTCtrLk=CreateLock("clkTCtrLk",9);
	sbTCtrLk=CreateLock("sbTCtrLk",8);

	/* Call testcase with minimum requiements */
	testcase12();

	StockBoy();
}
